﻿using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using EnvDTE90;
using System.Diagnostics;
using System.Collections.Generic;

using Microsoft.VisualStudio;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Data.Interop;

namespace Antix.VSAddIn
{
    [Serializable()]
    public abstract class Connect : IDTExtensibility2
    {
        #region Events Handlers

        public virtual void OnConnection(ext_ConnectMode connectMode, ref Array custom) { }
        public virtual void OnAddInsUpdate(ref Array custom) { }
        public virtual void OnBeginShutdown(ref Array custom) { }

        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            this.Application = (DTE2)application;
            this.AddInInstance = (AddIn)addInInst;

            this.CommandBars = new CommandBars(this);

            this.SelectedProjects = new List<Project>();

            // attach event handlers
            this._SelectionEvents = this.Application.Events.SelectionEvents;
            this._SelectionEvents.OnChange += new _dispSelectionEvents_OnChangeEventHandler(OnSelectionChange);

            this.OnConnection(connectMode, ref custom);

        }

        public virtual void OnStartupComplete(ref Array custom)
        {
        }

        public virtual void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
        {
            this.CommandBars.Dispose();
        }

        /// <summary>
        /// <para>Fires on any selection change</para>
        /// </summary>
        public virtual void OnSelectionChange()
        {
            // check for change in selected projects
            List<Project> projects = new List<Project>();
            bool changed = false;
            foreach (SelectedItem item in this.Application.SelectedItems)
            {
                Project selectedProject = item.Project ?? item.ProjectItem.ContainingProject;
                if (selectedProject != null)
                {
                    if (!this.SelectedProjects.Contains(selectedProject)) { changed = true; }
                    projects.Add(selectedProject);
                }
            }

            if (this.SelectedProjects.Count!=projects.Count) { changed = true; }
            this.SelectedProjects = projects;

            if(changed) { this.OnProjectSelected();}
        }

        /// <summary>
        /// <para>Fires when a project is newly selected or deselected</para>
        /// <para>Currently selected projects are available through the SelectedProjects list</para>
        /// </summary>
        public virtual void OnProjectSelected() { }

        #endregion

        #region Properties

        public DTE2 Application { get; private set; }
        public AddIn AddInInstance { get; private set; }

        public CommandBars CommandBars { get; private set; }

        public List<Project> SelectedProjects { get; private set; }

        private SelectionEvents _SelectionEvents;

        #endregion

        #region Methods

        /// <summary>
        /// <para>Get a service of the Type 'T'</para>
        /// </summary>
        /// <typeparam name="T">Service Type</typeparam>
        /// <returns>Service if found</returns>
        public T GetService<T>()
        {
            IOleServiceProvider serviceProvider = (IOleServiceProvider)this.Application;
            Guid serviceGuid = typeof(T).GUID;
            object service;

            serviceProvider.QueryService(ref serviceGuid, ref serviceGuid, out service);

            return (T)service;
        }

        public string GetConnectionString(bool loadSelection)
        {
            IVsDataConnectionDialogFactory factory = this.GetService<IVsDataConnectionDialogFactory>();
            IVsDataConnectionDialog dialog = factory.CreateConnectionDialog();
            try
            {
                dialog.AddAllSources();
                if (loadSelection) { dialog.LoadSourceSelection(); }
                if (dialog.ShowDialog())
                {
                    if(dialog.SaveSelection){dialog.SaveSourceSelection();}
                    return dialog.DisplayConnectionString;
                }

                return null;
            }
            finally
            {
                dialog.Dispose();
            }
        }

        public void ShowAddNewItemDialog(Project project, string templateName)
        {
            try
            {
                //this.Application.ExecuteCommand("Project.AddNewItem", templateName);
                this.Application.ExecuteCommand("Project.AddNewItem", string.Empty);
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry(
                    this.AddInInstance.Name, ex.ToString(),
                    EventLogEntryType.Error);
                throw;
            }

            #region tried - failed
            //string filter = String.Empty;
            //string browseLocations = string.Empty;
            //int dontShowAgain;
            //uint flags = (uint)
            //    (__VSADDITEMFLAGS.VSADDITEM_AddNewItems
            //    | __VSADDITEMFLAGS.VSADDITEM_SuggestTemplateName
            //    | __VSADDITEMFLAGS.VSADDITEM_AllowHiddenTreeView);

            //IVsHierarchy hierarchy = GetHierarchy(project);
            ////uint projectId;
            ////hierarchy.ParseCanonicalName(project.FullName, out projectId);

            // doesn't work
            //string[] rgpszFileToOpen = new string[] { templateName };
            //VSADDRESULT[] pResult = new VSADDRESULT[rgpszFileToOpen.Length];
            //((IVsProject)project)
            //    .AddItem(
            //        VSConstants.VSITEMID_ROOT, 
            //        VSADDITEMOPERATION.VSADDITEMOP_OPENFILE, 
            //        String.Empty, (uint)rgpszFileToOpen.Length, 
            //        rgpszFileToOpen, 
            //        IntPtr.Zero, 
            //        pResult);

            //System.Guid projectGuid;
            //hierarchy.GetGuidProperty(
            //        VSConstants.VSITEMID_ROOT,
            //        (int)__VSHPROPID.VSHPROPID_ProjectIDGuid,
            //        out projectGuid);

            // doesn't work
            //IVsAddProjectItemDlg2 dialog = this.GetService<IVsAddProjectItemDlg2>();
            //dialog.AddProjectItemDlg(
            //    projectId,
            //    ref projectGuid, (IVsProject)hierarchy, flags,
            //    null, null,
            //    ref browseLocations, ref filter,
            //    out dontShowAgain);

            // works on database project not on class library
            //IVsAddProjectItemDlg dialog = this.GetService<IVsAddProjectItemDlg>();
            //dialog.AddProjectItemDlg(
            //    projectId,
            //    ref projectGuid, (IVsProject3)hierarchy, flags,
            //    null, null,
            //    ref browseLocations, ref filter,
            //    out dontShowAgain);

            #endregion
        }

        #region Get/Set Property

        public IVsHierarchy GetHierarchy(Project project)
        {
            // get the hierarchy
            IVsHierarchy hierarchy;
            IVsSolution solution = GetService<IVsSolution>();
            solution.GetProjectOfUniqueName(project.UniqueName, out hierarchy);

            return hierarchy;
        }

        public void SetProperty(ProjectItem projectItem, string name, string value)
        {
            SetProperty(this.GetHierarchy(projectItem.ContainingProject), projectItem.Properties, name, value);
        }

        public void SetProperty(IVsHierarchy hierarchy, Properties props, string name, string value)
        {
            IVsBuildPropertyStorage buildPropertyStorage = hierarchy as IVsBuildPropertyStorage;
            if (buildPropertyStorage != null)
            {
                uint itemId;
                string fullPath = (string)props.Item("FullPath").Value;
                hierarchy.ParseCanonicalName(fullPath, out itemId);

                buildPropertyStorage.SetItemAttribute(itemId, name, value);
            }
        }

        public string GetProperty(ProjectItem projectItem, string name)
        {
            return GetProperty(this.GetHierarchy(projectItem.ContainingProject),projectItem.Properties, name);
        }

        public string GetProperty(IVsHierarchy hierarchy, Properties props,string name)
        {
            IVsBuildPropertyStorage buildPropertyStorage = hierarchy as IVsBuildPropertyStorage;

            string value = null;
            if (buildPropertyStorage != null)
            {
                uint itemId;
                string fullPath = (string)props.Item("FullPath").Value;
                hierarchy.ParseCanonicalName(fullPath, out itemId);

                buildPropertyStorage.GetItemAttribute(itemId, name, out value);

            }

            return value;
        }

        #endregion

        #region Debug

        public void DebugListProperties(ProjectItem projectItem)
        {
            foreach (Property prop in projectItem.Properties)
            {
                try
                {
                    Debug.Write(prop.Name);
                    Debug.Write(string.Format(": {1}\n", prop.Name, prop.Value));
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(string.Format("Exception: {0}", ex));
                }
            }

        }

        #endregion

        #endregion

    }
}